
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="zh_Hans">
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>内置的基于类的通用视图 &#8212; Django 3.2.11.dev 文档</title>
    <link rel="stylesheet" href="../../_static/default.css" type="text/css" />
    <link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
    <script type="text/javascript" id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
    <script type="text/javascript" src="../../_static/jquery.js"></script>
    <script type="text/javascript" src="../../_static/underscore.js"></script>
    <script type="text/javascript" src="../../_static/doctools.js"></script>
    <script type="text/javascript" src="../../_static/language_data.js"></script>
    <link rel="index" title="索引" href="../../genindex.html" />
    <link rel="search" title="搜索" href="../../search.html" />
    <link rel="next" title="使用基于类的视图处理表单" href="generic-editing.html" />
    <link rel="prev" title="基于类的视图" href="intro.html" />



 
<script src="../../templatebuiltins.js"></script>
<script>
(function($) {
    if (!django_template_builtins) {
       // templatebuiltins.js missing, do nothing.
       return;
    }
    $(document).ready(function() {
        // Hyperlink Django template tags and filters
        var base = "../../ref/templates/builtins.html";
        if (base == "#") {
            // Special case for builtins.html itself
            base = "";
        }
        // Tags are keywords, class '.k'
        $("div.highlight\\-html\\+django span.k").each(function(i, elem) {
             var tagname = $(elem).text();
             if ($.inArray(tagname, django_template_builtins.ttags) != -1) {
                 var fragment = tagname.replace(/_/, '-');
                 $(elem).html("<a href='" + base + "#" + fragment + "'>" + tagname + "</a>");
             }
        });
        // Filters are functions, class '.nf'
        $("div.highlight\\-html\\+django span.nf").each(function(i, elem) {
             var filtername = $(elem).text();
             if ($.inArray(filtername, django_template_builtins.tfilters) != -1) {
                 var fragment = filtername.replace(/_/, '-');
                 $(elem).html("<a href='" + base + "#" + fragment + "'>" + filtername + "</a>");
             }
        });
    });
})(jQuery);</script>

  </head><body>

    <div class="document">
  <div id="custom-doc" class="yui-t6">
    <div id="hd">
      <h1><a href="../../index.html">Django 3.2.11.dev 文档</a></h1>
      <div id="global-nav">
        <a title="Home page" href="../../index.html">Home</a>  |
        <a title="Table of contents" href="../../contents.html">Table of contents</a>  |
        <a title="Global index" href="../../genindex.html">Index</a>  |
        <a title="Module index" href="../../py-modindex.html">Modules</a>
      </div>
      <div class="nav">
    &laquo; <a href="intro.html" title="基于类的视图">previous</a>
     |
    <a href="../index.html" title="使用 Django" accesskey="U">up</a>
   |
    <a href="generic-editing.html" title="使用基于类的视图处理表单">next</a> &raquo;</div>
    </div>

    <div id="bd">
      <div id="yui-main">
        <div class="yui-b">
          <div class="yui-g" id="topics-class-based-views-generic-display">
            
  <div class="section" id="s-built-in-class-based-generic-views">
<span id="built-in-class-based-generic-views"></span><h1>内置的基于类的通用视图<a class="headerlink" href="#built-in-class-based-generic-views" title="永久链接至标题">¶</a></h1>
<p>编写 web 应用会很单调，因为我们一遍一遍地重复一些模式。Django 试图在模型和模板层面消除一些单调，但 web 开发者们仍然会在视图层感受到这种无聊。</p>
<p>Django 通用视图是为了缓解这种情况而被开发的。他们采用在视图开发时发现的某些通用的风格和模式，并把它们抽象化，因此你可能更快的编写公共的数据视图，而不是编写更多的代码。</p>
<p>我们可以识别出某些通用任务，比如显示对象列表，编写显示任何对象列表的代码。然后有问题的模型将被当做附加的参数传递给 URLconf。</p>
<p>Django 附带通用视图来执行以下操作：</p>
<ul class="simple">
<li>为单个对象显示列表和详情页。如果我们创建一个管理会议的应用，那么 <code class="docutils literal notranslate"><span class="pre">TalkListView</span></code> 和 <code class="docutils literal notranslate"><span class="pre">RegisteredUserListView</span></code> 就是列表视图的例子。单个&quot;话题页&quot;将作为例子中的&quot;详情页&quot;。</li>
<li>在年/月/日的归档页面，相关的详情和最新页面将显示基于日期的对象。</li>
<li>运行用户创建、更新和删除对象——无论是否授权。</li>
</ul>
<p>总之，这些视图提供的接口来执行开发者们遇到的最常见的通用任务。</p>
<div class="section" id="s-extending-generic-views">
<span id="extending-generic-views"></span><h2>扩展通用视图<a class="headerlink" href="#extending-generic-views" title="永久链接至标题">¶</a></h2>
<p>毫无疑问，使用通用视图可以大大加快开发速度。然而，在很多项目中，会出现通用视图不再适用。实际上，很多新手 Django 开发者问的最常见的问题是怎么让通用视图处理更大范围的情况。</p>
<p>这是通用视图在1.3版重新设计的原因之一——之前，它们只是具有各种选项的视图函数；现在，扩展通用视图的推荐方法是将它们子类化并且覆盖它们的属性或方法，而不是在 URLconf 中传递一个很庞杂的配置。</p>
<p>也就是说，通用视图有一个限制。如果你发现很难将实现的视图作为通用视图的子类，那么你可能会发现使用自己的基类或函数视图来编写你所需的代码会更有效率。</p>
<p>一些第三方应用里提供了很多通用视图案例，或者你可以编写你需要的通用视图。</p>
</div>
<div class="section" id="s-generic-views-of-objects">
<span id="generic-views-of-objects"></span><h2>对象的通用视图<a class="headerlink" href="#generic-views-of-objects" title="永久链接至标题">¶</a></h2>
<p><a class="reference internal" href="../../ref/class-based-views/base.html#django.views.generic.base.TemplateView" title="django.views.generic.base.TemplateView"><code class="xref py py-class docutils literal notranslate"><span class="pre">TemplateView</span></code></a> 当然也很常用，但 Django 通用视图在呈现数据库内容视图时确实很出色。因为这是一个很常见任务，Django 附带一些内置的通用视图来协助生成列表和对象的详情视图。</p>
<p>让我们首先看一些显示对象列表或单独对象的例子。</p>
<p>我们将使用这些模型：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># models.py</span>
<span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>

<span class="k">class</span> <span class="nc">Publisher</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
    <span class="n">address</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
    <span class="n">city</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">60</span><span class="p">)</span>
    <span class="n">state_province</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
    <span class="n">country</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
    <span class="n">website</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">URLField</span><span class="p">()</span>

    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
        <span class="n">ordering</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;-name&quot;</span><span class="p">]</span>

    <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>

<span class="k">class</span> <span class="nc">Author</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="n">salutation</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
    <span class="n">email</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">EmailField</span><span class="p">()</span>
    <span class="n">headshot</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ImageField</span><span class="p">(</span><span class="n">upload_to</span><span class="o">=</span><span class="s1">&#39;author_headshots&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>

<span class="k">class</span> <span class="nc">Book</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="n">title</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
    <span class="n">authors</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ManyToManyField</span><span class="p">(</span><span class="s1">&#39;Author&#39;</span><span class="p">)</span>
    <span class="n">publisher</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="n">Publisher</span><span class="p">,</span> <span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">CASCADE</span><span class="p">)</span>
    <span class="n">publication_date</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">DateField</span><span class="p">()</span>
</pre></div>
</div>
<p>现在我们需要定义一个视图：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># views.py</span>
<span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">ListView</span>
<span class="kn">from</span> <span class="nn">books.models</span> <span class="kn">import</span> <span class="n">Publisher</span>

<span class="k">class</span> <span class="nc">PublisherListView</span><span class="p">(</span><span class="n">ListView</span><span class="p">):</span>
    <span class="n">model</span> <span class="o">=</span> <span class="n">Publisher</span>
</pre></div>
</div>
<p>最后将这个视图挂钩到你的urls：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># urls.py</span>
<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
<span class="kn">from</span> <span class="nn">books.views</span> <span class="kn">import</span> <span class="n">PublisherListView</span>

<span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">path</span><span class="p">(</span><span class="s1">&#39;publishers/&#39;</span><span class="p">,</span> <span class="n">PublisherListView</span><span class="o">.</span><span class="n">as_view</span><span class="p">()),</span>
<span class="p">]</span>
</pre></div>
</div>
<p>这就是我们需要编写的所有代码。尽管我们仍然需要编写一个模板。我们可以给视图添加 <code class="docutils literal notranslate"><span class="pre">template_name</span></code> 属性来告诉视图使用哪个模板，但如果没有明确的模板，Django 将从对象名称中推断一个。在这个例子中，推断模板将是 <code class="docutils literal notranslate"><span class="pre">&quot;books/publisher_list.html&quot;</span></code> —— &quot;books&quot; 部分来自定义模型所属 app 名称，而 &quot;publisher&quot; 必须是模型名称的小写。</p>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p class="last">因此，假如一个 <code class="docutils literal notranslate"><span class="pre">DjangoTemplates</span></code>&nbsp;后端的 <code class="docutils literal notranslate"><span class="pre">APP_DIRS</span></code>&nbsp;选项在 <a class="reference internal" href="../../ref/settings.html#std:setting-TEMPLATES"><code class="xref std std-setting docutils literal notranslate"><span class="pre">TEMPLATES</span></code></a> 中被设为 True 时，模板地址将会是 /path/to/project/books/templates/books/publisher_list.html 。</p>
</div>
<p>这个模板将针对变量名为 <code class="docutils literal notranslate"><span class="pre">object_list</span></code> 的上下文进行渲染，这个变量包含所有的出版者对象。模板可以是这个样子：</p>
<div class="highlight-html+django notranslate"><div class="highlight"><pre><span></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;base.html&quot;</span> <span class="cp">%}</span>

<span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}</span>
    <span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span>Publishers<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
        <span class="cp">{%</span> <span class="k">for</span> <span class="nv">publisher</span> <span class="k">in</span> <span class="nv">object_list</span> <span class="cp">%}</span>
            <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span><span class="cp">{{</span> <span class="nv">publisher.name</span> <span class="cp">}}</span><span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
        <span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
    <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
</div>
<p>这就是全部内容。通用视图的所有炫酷功能来自改变通用视图上的属性设置。<a class="reference internal" href="../../ref/class-based-views/index.html"><span class="doc">generic views reference</span></a> 文档里有所有的通用视图和选项的详细说明；这篇文档剩下的部分将介绍一些你可能需要自定义和扩展通用视图的常用办法。</p>
<div class="section" id="s-making-friendly-template-contexts">
<span id="making-friendly-template-contexts"></span><h3>制作&quot;友好&quot;的模板上下文<a class="headerlink" href="#making-friendly-template-contexts" title="永久链接至标题">¶</a></h3>
<p>你可能已经注意到了例子中的出版者列表模板在变量名为 <code class="docutils literal notranslate"><span class="pre">object_list</span></code> 里保存了所有的出版者。尽管它工作正常，但它对模板作者并不是特别友好：他们必须在这里处理出版者信息。</p>
<p>如果你正在处理模型对象，这已经完成了。当你正在处理对象或查询，Django 使用小写的模型类名来填充上下文。这是除了默认的 <code class="docutils literal notranslate"><span class="pre">object_list</span></code> 类目之外提供的，但包含完全相同的数据，即 <code class="docutils literal notranslate"><span class="pre">publisher_list</span></code>。</p>
<p>如果仍然匹配的不好，你可以手工设置上下文变量的名称。通用视图上的``context_object_name`` 属性指定要使用的上下文变量：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># views.py</span>
<span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">ListView</span>
<span class="kn">from</span> <span class="nn">books.models</span> <span class="kn">import</span> <span class="n">Publisher</span>

<span class="k">class</span> <span class="nc">PublisherListView</span><span class="p">(</span><span class="n">ListView</span><span class="p">):</span>
    <span class="n">model</span> <span class="o">=</span> <span class="n">Publisher</span>
    <span class="n">context_object_name</span> <span class="o">=</span> <span class="s1">&#39;my_favorite_publishers&#39;</span>
</pre></div>
</div>
<p>提供有用的 <code class="docutils literal notranslate"><span class="pre">context_object_name</span></code> 总是一个好主意。设计模板的合作者将会感激你。</p>
</div>
<div class="section" id="s-adding-extra-context">
<span id="s-id1"></span><span id="adding-extra-context"></span><span id="id1"></span><h3>添加额外的上下文<a class="headerlink" href="#adding-extra-context" title="永久链接至标题">¶</a></h3>
<p>通常，你只需要提供通用视图所提供的信息之外的一些附加的信息。比如，打算在每一个出版者详情页上显示所有的书籍列表。<a class="reference internal" href="../../ref/class-based-views/generic-display.html#django.views.generic.detail.DetailView" title="django.views.generic.detail.DetailView"><code class="xref py py-class docutils literal notranslate"><span class="pre">DetailView</span></code></a> 通用视图提供出版者至上下文，但是怎么在模板里获取更多的信息呢？</p>
<p>答案是子类化 <a class="reference internal" href="../../ref/class-based-views/generic-display.html#django.views.generic.detail.DetailView" title="django.views.generic.detail.DetailView"><code class="xref py py-class docutils literal notranslate"><span class="pre">DetailView</span></code></a> ，并提供你实现的 <code class="docutils literal notranslate"><span class="pre">get_context_data</span></code> 方法。默认的实现只是将正在显示的对象增加到模板，但你需要覆盖它来发送更多信息：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">DetailView</span>
<span class="kn">from</span> <span class="nn">books.models</span> <span class="kn">import</span> <span class="n">Book</span><span class="p">,</span> <span class="n">Publisher</span>

<span class="k">class</span> <span class="nc">PublisherDetailView</span><span class="p">(</span><span class="n">DetailView</span><span class="p">):</span>

    <span class="n">model</span> <span class="o">=</span> <span class="n">Publisher</span>

    <span class="k">def</span> <span class="nf">get_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="c1"># Call the base implementation first to get a context</span>
        <span class="n">context</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_context_data</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="c1"># Add in a QuerySet of all the books</span>
        <span class="n">context</span><span class="p">[</span><span class="s1">&#39;book_list&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">context</span>
</pre></div>
</div>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p>通常，<code class="docutils literal notranslate"><span class="pre">get_context_data</span></code> 将合并当前类的所有父类的上下文数据。要在你想要改变上下文的类中保留此行为，你应该确保在超类上调用了 <code class="docutils literal notranslate"><span class="pre">get_context_data</span></code> 。当没有两个类尝试去定义相同的键是，会给出正确的结果。然而，如果任何类打算在父类已经设置键（调用super后）后覆盖键，如果任何子类想确保覆盖了所有父类，那么就需要在调用super后显式地设置它。如果有问题，请查看视图的方法解析顺序。</p>
<p class="last">另一个考虑是来自基于类的通用视图的上下文数据将覆盖由上下文处理器提供的数据；可以查看 <a class="reference internal" href="../../ref/class-based-views/mixins-single-object.html#django.views.generic.detail.SingleObjectMixin.get_context_data" title="django.views.generic.detail.SingleObjectMixin.get_context_data"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_context_data()</span></code></a> 的例子。</p>
</div>
</div>
<div class="section" id="s-viewing-subsets-of-objects">
<span id="s-generic-views-list-subsets"></span><span id="viewing-subsets-of-objects"></span><span id="generic-views-list-subsets"></span><h3>查看对象的子集<a class="headerlink" href="#viewing-subsets-of-objects" title="永久链接至标题">¶</a></h3>
<p>现在让我们仔细观察我们一直在使用的 <code class="docutils literal notranslate"><span class="pre">model</span></code>&nbsp;参数。<code class="docutils literal notranslate"><span class="pre">model</span></code> 参数指定了视图将对其进行操作的数据模型，可用于对单个对象或对象集合进行操作的所有通用视图上。然而，<code class="docutils literal notranslate"><span class="pre">model</span></code> 参数不仅仅用来指定视图操作对象，还可以使用 <code class="docutils literal notranslate"><span class="pre">queryset</span></code> 参数指定对象列表。</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">DetailView</span>
<span class="kn">from</span> <span class="nn">books.models</span> <span class="kn">import</span> <span class="n">Publisher</span>

<span class="k">class</span> <span class="nc">PublisherDetailView</span><span class="p">(</span><span class="n">DetailView</span><span class="p">):</span>

    <span class="n">context_object_name</span> <span class="o">=</span> <span class="s1">&#39;publisher&#39;</span>
    <span class="n">queryset</span> <span class="o">=</span> <span class="n">Publisher</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
</pre></div>
</div>
<p>指定 <code class="docutils literal notranslate"><span class="pre">model</span> <span class="pre">=</span> <span class="pre">Publisher</span></code> 只是 <code class="docutils literal notranslate"><span class="pre">queryset</span> <span class="pre">=</span> <span class="pre">Publisher.objects.all()</span></code> 的简写。然而，通过使用 <code class="docutils literal notranslate"><span class="pre">queryset</span></code> 定义过滤的对象列表，你可以更加具体的了解在视图中可见的对象。（查看 <a class="reference internal" href="../db/queries.html"><span class="doc">执行查询</span></a> 来获取更多 <a class="reference internal" href="../../ref/models/querysets.html#django.db.models.query.QuerySet" title="django.db.models.query.QuerySet"><code class="xref py py-class docutils literal notranslate"><span class="pre">QuerySet</span></code></a> 对象的信息，查看 <a class="reference internal" href="../../ref/class-based-views/index.html"><span class="doc">class-based views reference</span></a> 来获取完整信息）</p>
<p>举一个例子，我们想通过出版日期排序一个书籍列表，最新的排第一：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">ListView</span>
<span class="kn">from</span> <span class="nn">books.models</span> <span class="kn">import</span> <span class="n">Book</span>

<span class="k">class</span> <span class="nc">BookListView</span><span class="p">(</span><span class="n">ListView</span><span class="p">):</span>
    <span class="n">queryset</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;-publication_date&#39;</span><span class="p">)</span>
    <span class="n">context_object_name</span> <span class="o">=</span> <span class="s1">&#39;book_list&#39;</span>
</pre></div>
</div>
<p>这是一个很简单的例子，但很好的说明了问题。你通常会想做比重新排序对象更多的操作。如果你想显示特定出版者的书籍列表，你可以使用相同技术：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">ListView</span>
<span class="kn">from</span> <span class="nn">books.models</span> <span class="kn">import</span> <span class="n">Book</span>

<span class="k">class</span> <span class="nc">AcmeBookListView</span><span class="p">(</span><span class="n">ListView</span><span class="p">):</span>

    <span class="n">context_object_name</span> <span class="o">=</span> <span class="s1">&#39;book_list&#39;</span>
    <span class="n">queryset</span> <span class="o">=</span> <span class="n">Book</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">publisher__name</span><span class="o">=</span><span class="s1">&#39;ACME Publishing&#39;</span><span class="p">)</span>
    <span class="n">template_name</span> <span class="o">=</span> <span class="s1">&#39;books/acme_list.html&#39;</span>
</pre></div>
</div>
<p>注意，和过滤的查询结果一起，我们还要指定自定义的模板名称。如果我们不这么做，通用视图将使用与 &quot;vanilla&quot; 对象列表相同的模板，这可能不是我们想要的。</p>
<p>还需要注意，这不是一个特别优雅的获取指定出版者书籍的方法。如果你想添加其他出版者页面，我们需要在URLconf中再添加几行，但如果多个出版者，这就变得不合理了。我们将在下一个部分来处理这个问题。</p>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p class="last">如果你在请求 <code class="docutils literal notranslate"><span class="pre">/books/acme/</span></code> 时得到了404页面，请检查确保有叫 'ACME Publishing' 的出版者。通用视图有一个 <code class="docutils literal notranslate"><span class="pre">allow_empty</span></code> 参数来解决这个问题。查看 <a class="reference internal" href="../../ref/class-based-views/index.html"><span class="doc">class-based-views reference</span></a> 来获取更多细节。</p>
</div>
</div>
<div class="section" id="s-dynamic-filtering">
<span id="dynamic-filtering"></span><h3>动态过滤<a class="headerlink" href="#dynamic-filtering" title="永久链接至标题">¶</a></h3>
<p>其他常见需求是通过URL中的某个键来过滤列表页面里的对象。之前我们在URLconf中硬编码了出版者的名字，但如果我们想编写一个显示任意出版者书籍的视图呢？</p>
<p>我们可以方便地覆盖 <code class="docutils literal notranslate"><span class="pre">ListView</span></code> 的 <a class="reference internal" href="../../ref/class-based-views/mixins-multiple-object.html#django.views.generic.list.MultipleObjectMixin.get_queryset" title="django.views.generic.list.MultipleObjectMixin.get_queryset"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_queryset()</span></code></a> 方法。默认情况下，它返回 <code class="docutils literal notranslate"><span class="pre">queryset</span></code> 属性值，但现在我们可以用它来添加更多逻辑。</p>
<p>这项工作的关键部分是当基于类的视图被调用的时候，各种常用的东西被存储在 <code class="docutils literal notranslate"><span class="pre">self</span></code> 上，而且请求  (<code class="docutils literal notranslate"><span class="pre">self.request</span></code>)&nbsp;根据 URLconf 抓取位置(<code class="docutils literal notranslate"><span class="pre">self.args</span></code>) 和基于名称 (<code class="docutils literal notranslate"><span class="pre">self.kwargs</span></code>)&nbsp;的参数。</p>
<p>现在，我们有个带有单个抓取组的URLconf。</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># urls.py</span>
<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
<span class="kn">from</span> <span class="nn">books.views</span> <span class="kn">import</span> <span class="n">PublisherBookListView</span>

<span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">path</span><span class="p">(</span><span class="s1">&#39;books/&lt;publisher&gt;/&#39;</span><span class="p">,</span> <span class="n">PublisherBookListView</span><span class="o">.</span><span class="n">as_view</span><span class="p">()),</span>
<span class="p">]</span>
</pre></div>
</div>
<p>接下来，我们将编写 <code class="docutils literal notranslate"><span class="pre">PublisherBookListView</span></code> 视图本身：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># views.py</span>
<span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">get_object_or_404</span>
<span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">ListView</span>
<span class="kn">from</span> <span class="nn">books.models</span> <span class="kn">import</span> <span class="n">Book</span><span class="p">,</span> <span class="n">Publisher</span>

<span class="k">class</span> <span class="nc">PublisherBookListView</span><span class="p">(</span><span class="n">ListView</span><span class="p">):</span>

    <span class="n">template_name</span> <span class="o">=</span> <span class="s1">&#39;books/books_by_publisher.html&#39;</span>

    <span class="k">def</span> <span class="nf">get_queryset</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">publisher</span> <span class="o">=</span> <span class="n">get_object_or_404</span><span class="p">(</span><span class="n">Publisher</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;publisher&#39;</span><span class="p">])</span>
        <span class="k">return</span> <span class="n">Book</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">publisher</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">publisher</span><span class="p">)</span>
</pre></div>
</div>
<p>可以很方便地使用 <code class="docutils literal notranslate"><span class="pre">get_queryset</span></code> 来给查询集添加逻辑。比如，我们可以使用 <code class="docutils literal notranslate"><span class="pre">self.request.user</span></code> 来过滤当前用户或其他更复杂的逻辑。</p>
<p>我们也可以同时添加出版者到上下文中，因此我们能在模板中使用它：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>

<span class="k">def</span> <span class="nf">get_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="c1"># Call the base implementation first to get a context</span>
    <span class="n">context</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_context_data</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
    <span class="c1"># Add in the publisher</span>
    <span class="n">context</span><span class="p">[</span><span class="s1">&#39;publisher&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">publisher</span>
    <span class="k">return</span> <span class="n">context</span>
</pre></div>
</div>
</div>
<div class="section" id="s-performing-extra-work">
<span id="s-generic-views-extra-work"></span><span id="performing-extra-work"></span><span id="generic-views-extra-work"></span><h3>执行额外的任务<a class="headerlink" href="#performing-extra-work" title="永久链接至标题">¶</a></h3>
<p>最后一个常见模式里，我们将看到涉及在调用通用视图前后执行一些附加任务。</p>
<p>想象在  <code class="docutils literal notranslate"><span class="pre">Author</span></code>  模型上有一个 <code class="docutils literal notranslate"><span class="pre">last_accessed</span></code>&nbsp;字段，用来查看谁是最新查看作者的人：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># models.py</span>
<span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>

<span class="k">class</span> <span class="nc">Author</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="n">salutation</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
    <span class="n">email</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">EmailField</span><span class="p">()</span>
    <span class="n">headshot</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ImageField</span><span class="p">(</span><span class="n">upload_to</span><span class="o">=</span><span class="s1">&#39;author_headshots&#39;</span><span class="p">)</span>
    <span class="n">last_accessed</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">DateTimeField</span><span class="p">()</span>
</pre></div>
</div>
<p>通用的 <code class="docutils literal notranslate"><span class="pre">DetailView</span></code> 类不知道关于这个字段的任何信息，但我们可以再次编写自定义视图来保持字段更新。</p>
<p>首先，我们需要在URLconf中添加一个作者详情的url指向自定义视图：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
<span class="kn">from</span> <span class="nn">books.views</span> <span class="kn">import</span> <span class="n">AuthorDetailView</span>

<span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
    <span class="c1">#...</span>
    <span class="n">path</span><span class="p">(</span><span class="s1">&#39;authors/&lt;int:pk&gt;/&#39;</span><span class="p">,</span> <span class="n">AuthorDetailView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;author-detail&#39;</span><span class="p">),</span>
<span class="p">]</span>
</pre></div>
</div>
<p>然后我们编写一个新的视图——<code class="docutils literal notranslate"><span class="pre">get_object</span></code> 来查找对象——因此我们可以覆盖它并包装调用：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.utils</span> <span class="kn">import</span> <span class="n">timezone</span>
<span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">DetailView</span>
<span class="kn">from</span> <span class="nn">books.models</span> <span class="kn">import</span> <span class="n">Author</span>

<span class="k">class</span> <span class="nc">AuthorDetailView</span><span class="p">(</span><span class="n">DetailView</span><span class="p">):</span>

    <span class="n">queryset</span> <span class="o">=</span> <span class="n">Author</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">get_object</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">obj</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_object</span><span class="p">()</span>
        <span class="c1"># Record the last accessed date</span>
        <span class="n">obj</span><span class="o">.</span><span class="n">last_accessed</span> <span class="o">=</span> <span class="n">timezone</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
        <span class="n">obj</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">obj</span>
</pre></div>
</div>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p>URLconf 在这里使用组 <code class="docutils literal notranslate"><span class="pre">pk</span></code> ，这个名字是 <code class="docutils literal notranslate"><span class="pre">DetailView</span></code> 用来查找过滤查询集的主键值的默认名称。</p>
<p class="last">如果你想调用组别的东西，你可以在视图上设置 <a class="reference internal" href="../../ref/class-based-views/mixins-single-object.html#django.views.generic.detail.SingleObjectMixin.pk_url_kwarg" title="django.views.generic.detail.SingleObjectMixin.pk_url_kwarg"><code class="xref py py-attr docutils literal notranslate"><span class="pre">pk_url_kwarg</span></code></a>。</p>
</div>
</div>
</div>
</div>


          </div>
        </div>
      </div>
      
        
          <div class="yui-b" id="sidebar">
            
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
  <h3><a href="../../contents.html">Table of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">内置的基于类的通用视图</a><ul>
<li><a class="reference internal" href="#extending-generic-views">扩展通用视图</a></li>
<li><a class="reference internal" href="#generic-views-of-objects">对象的通用视图</a><ul>
<li><a class="reference internal" href="#making-friendly-template-contexts">制作&quot;友好&quot;的模板上下文</a></li>
<li><a class="reference internal" href="#adding-extra-context">添加额外的上下文</a></li>
<li><a class="reference internal" href="#viewing-subsets-of-objects">查看对象的子集</a></li>
<li><a class="reference internal" href="#dynamic-filtering">动态过滤</a></li>
<li><a class="reference internal" href="#performing-extra-work">执行额外的任务</a></li>
</ul>
</li>
</ul>
</li>
</ul>

  <h4>上一个主题</h4>
  <p class="topless"><a href="intro.html"
                        title="上一章">基于类的视图</a></p>
  <h4>下一个主题</h4>
  <p class="topless"><a href="generic-editing.html"
                        title="下一章">使用基于类的视图处理表单</a></p>
  <div role="note" aria-label="source link">
    <h3>本页</h3>
    <ul class="this-page-menu">
      <li><a href="../../_sources/topics/class-based-views/generic-display.txt"
            rel="nofollow">显示源代码</a></li>
    </ul>
   </div>
<div id="searchbox" style="display: none" role="search">
  <h3>快速搜索</h3>
    <div class="searchformwrapper">
    <form class="search" action="../../search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="转向" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    </div>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
              <h3>Last update:</h3>
              <p class="topless">12月 07, 2021</p>
          </div>
        
      
    </div>

    <div id="ft">
      <div class="nav">
    &laquo; <a href="intro.html" title="基于类的视图">previous</a>
     |
    <a href="../index.html" title="使用 Django" accesskey="U">up</a>
   |
    <a href="generic-editing.html" title="使用基于类的视图处理表单">next</a> &raquo;</div>
    </div>
  </div>

      <div class="clearer"></div>
    </div>
  </body>
</html>